/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package proyectoarboles;

import java.awt.Color;
import java.awt.Graphics;

/**
 * Clase encargada de dibujar el arbol basado en el plano de corte
 * @author Lucho
 */
public class Arbol {

    private Nodo principal;

    /**
     * Constructor por defecto de la clase
     */
    public Arbol() {
        principal = null;
    }

    /**
     * Constructor con el atributo del nodo
     * @param nodo
     */
    public Arbol(Nodo nodo){
        principal = nodo;
    }


    /**
     * Constructor para dibujar un arbol con sus arboles hijo por derecha y por
     * izquierda
     * @param dato
     * @param izquierda
     * @param derecha
     */
    public Arbol(String dato, Arbol izquierda, Arbol derecha){
        principal = new Nodo(dato);
        //Llenamos los datos del nodo de la izquierda
        if(izquierda != null){
            principal.setIzquierda(izquierda.getPrincipal());
        }else{
            principal.setIzquierda(null);
        }
        //Llenamos los datos del nodo de la derecha
        if(derecha != null){
            principal.setDerecha(derecha.getPrincipal());
        }else{
            principal.setDerecha(null);
        }
    }

    /**
     * Método para obtener el arbol de la izquierda del nodo principal
     * @return Arbol de la izquierda
     */
    public Arbol getArbolIzquierda(){
        return new Arbol(principal.getIzquierda());
    }


    /**
     * Método para obtener el arbol de la derecha del nodo principal
     * @return Arbol de la derecha
     */
    public Arbol getArbolDerecha(){
        return new Arbol(principal.getDerecha());
    }

    /**
    * Método para calcular la altura del arbol
    * @return Altura arbol
    */
    public int getAlturaArbol(){
        //Si el null el principal la altura del arbol es 0
        if(principal == null){									
            return 0;										
        }
        int derecha = 0;										
        int izquierda = 0;
        //Calculamos la altura del arbol de la izquierda
        if(principal.getIzquierda() != null){						
            izquierda = getArbolIzquierda().getAlturaArbol();
        }
        //Calculamos la altura del arbol de la derecha
        if(principal.getDerecha() != null){
            derecha = getArbolDerecha().getAlturaArbol();
        }
        //Retornamos la maxima altura entre el nodo de la derecha y el de la izquierda
        return Math.max(derecha+1,izquierda+1);
    }  

    /**
     * Metodo para obtener el numero de nodos del arbol
     * @return Cantidad de nodos del arbol
     */
    public int getNumeroNodos(){
        if(principal == null){
            return 0;
        }
        int izquierda = 0;
        int derecha = 0;
        izquierda = getArbolIzquierda().getNumeroNodos();
        derecha = getArbolDerecha().getNumeroNodos();
        return (izquierda + derecha) + 1;
    }  

    /**
     * Método que tiene como fin dibujar el arbol
     * @param graphics
     * @param izquierda
     * @param centrado
     * @param tamanio
     * @param espacioX
     * @param espacioY
     */
    public void dibujarArbol(Graphics graphics, int izquierda, int centrado, int tamanio, int espacioX, int espacioY){
            dibujarNodo(graphics,izquierda,centrado,tamanio,espacioX,espacioY, 0, 0,principal);
            dibujarLineasNodo(graphics, principal,principal,tamanio);										// draw the nodes and lines
    }

    /**
     * Método recursivo para dibujar las lineas que unen los nodos
     * @param graphics
     * @param nodo
     * @param ultimoNodo
     * @param tamanio
     */
    private void dibujarLineasNodo(Graphics graphics, Nodo nodo, Nodo ultimoNodo, int tamanio){
        //Validamos que el nodo no sea null para dibujar la lineas
        if(nodo != null){
            Punto centro = nodo.getCentro();
            Punto ultimoPunto = null;
            graphics.setColor(Color.BLUE);
            if(ultimoNodo != null){
                ultimoPunto = ultimoNodo.getCentro();
                graphics.drawLine(centro.coordenadaX, centro.coordenadaY,
                        ultimoPunto.coordenadaX, ultimoPunto.coordenadaY);
            }

            //Si es un nodo hoja de dibujamos un cuadrado, si no dibujamos un cuadrado,
            //para representar el nodo inicial
            if(nodo.esNodoHoja()){
                dibujarCuadrado(graphics, centro, tamanio);
            }else{
                 dibujarCirculoArbol(graphics, centro, tamanio);
            }
            //Si el ultimo punto del nodo existe,seguimos dibujando más circulos del nodo
            if(ultimoPunto != null){
                dibujarCirculoArbol(graphics, ultimoPunto, tamanio);
                textoNodo(graphics, ultimoNodo, ultimoPunto, tamanio);
            }
            //Dibujamos el texto del nodo en el centro de la figura correspondiente
            textoNodo(graphics, nodo, centro, tamanio);
            ultimoNodo = nodo;
            //Llamamos recursivamente para que se dibujen la lineas que unen los nodos
            //por izquierda y por derecha
            dibujarLineasNodo(graphics, nodo.getIzquierda(),ultimoNodo, tamanio);
            dibujarLineasNodo(graphics, nodo.getDerecha(),ultimoNodo, tamanio);
        }
    }  

    /**
     * Método para crear el texto dentro del nodo del arbol, lo únicos textos que
     * se muestran son: |, - y la letras correspondientes
     * @param graphics
     * @param nodo
     * @param center
     * @param tamanio
     */
    private void textoNodo(Graphics graphics, Nodo nodo, Punto centro, int tamanio){
        int tamanioMedio = (int) tamanio / Constantes.LINEADIVISORA;
        graphics.setColor(Color.BLACK);
        String datoNodo = nodo.getDato();
        switch (datoNodo) {
            case "-":
                //Dibujamos la linea horizontal dentro del ciruclo del árbol
                graphics.drawLine(centro.coordenadaX - tamanioMedio, centro.coordenadaY, centro.coordenadaX + tamanioMedio, centro.coordenadaY);
                break;
            case "|":
                //Dibujamos la linea vertical dentro del ciruclo del árbol
                graphics.drawLine(centro.coordenadaX, centro.coordenadaY + tamanioMedio, centro.coordenadaX, centro.coordenadaY - tamanioMedio);
                break;
            default:
                //Dibujamos la linea horizontal dentro del ciruclo del árbol
                graphics.drawString("" + nodo.getDato(), centro.coordenadaX, centro.coordenadaY+5);
                break;
        }
    }


    /**
     * Método para dibujar el cuadrado donde se van a ubicar los nodos hoja del plano
     * @param graphics
     * @param centro
     * @param tamanio
     */
    private void dibujarCuadrado(Graphics graphics, Punto centro, int tamanio){
        int tamanioMedio = tamanio / 2;
        graphics.setColor(Color.WHITE);
        graphics.fillRect(centro.coordenadaX - tamanioMedio, centro.coordenadaY - tamanioMedio,tamanio,tamanio);
        graphics.setColor(Color.BLACK);
        graphics.drawRect(centro.coordenadaX - tamanioMedio, centro.coordenadaY - tamanioMedio,tamanio,tamanio);
    } 


    /**
     * Método para dibujar el circulo del arbol
     * @param graphics
     * @param centro
     * @param tamanio
     */
    private void dibujarCirculoArbol(Graphics graphics, Punto centro, int tamanio){
        int tamanioMedio = tamanio / 2;										
        graphics.setColor(Color.WHITE);
        graphics.fillArc(centro.coordenadaX - tamanioMedio, centro.coordenadaY - tamanioMedio, tamanio,tamanio, 0, 360);
        graphics.setColor(Color.BLACK);
        graphics.drawArc(centro.coordenadaX - tamanioMedio, centro.coordenadaY - tamanioMedio, tamanio, tamanio, 0, 360);
    }


    /**
     * Método recursivo para dibujar el nodo
     * @param graphics
     * @param izquierda
     * @param top
     * @param tamanio
     * @param espacioX
     * @param espacioY
     * @param altura
     * @param contador
     * @param nodo
     * @return
     */
    private int dibujarNodo(Graphics graphics, int izquierda, int top, int tamanio,
            int espacioX, int espacioY, int altura,
                    int contador, Nodo nodo){
        if(nodo == null){
            return contador;
        }
        //Dibujamos primero los nodos de la izquierda
        contador = dibujarNodo(graphics,izquierda, top, tamanio, espacioX,espacioY, altura + 1, contador, nodo.getIzquierda());
        contador = calcularLocalizacionNodo(graphics,izquierda, top, tamanio, espacioX,espacioY,altura, contador, nodo);
        //Dibujamos los nodos de la derecha
        contador = dibujarNodo(graphics, izquierda, top,tamanio, espacioX,espacioY,altura + 1,contador, nodo.getDerecha());
        return contador;						
    } 



    /**
     * Método para calcular la localización nodo
     * @param graphics
     * @param izquierda
     * @param superior
     * @param tamanio
     * @param espacioX
     * @param espacioY
     * @param altura
     * @param contador
     * @param nodo
     * @return
     */
    private int calcularLocalizacionNodo(Graphics graphics, int izquierda, int superior, int tamanio,
        int espacioX, int espacioY, int altura,
                int contador, Nodo nodo){
        //Calcula el top de la izquierda
        Punto superiorIzquierda = getPuntoArbolSuperiorIzquierdo(izquierda, superior, espacioX,espacioY,contador,altura);
        //Calculamos el centro de la localización del nodo
        Punto centro = getPuntoCentro(superiorIzquierda.coordenadaX, superiorIzquierda.coordenadaY,tamanio);
        nodo.setCentro(centro);
        return contador + 1;
    }


   /**
    * Método para crear el punto del arbol superior izquierda
    * @param izquierda
    * @param superior
    * @param espacioX
    * @param espacioY
    * @param contador
    * @param altura
    * @return Objeto con el punto
    */
    private Punto getPuntoArbolSuperiorIzquierdo(int izquierda, int superior, int espacioX, int espacioY,
        int contador, int altura){
        int x = izquierda + (espacioX * contador);
        int y = superior + (espacioY * altura);
        return new Punto(x, y);	
    } 


    /**
     * Método para calcular el centro de dos puntos x y y
     * @param x
     * @param y
     * @param tamanio
     * @return Objeto punto con el centro
     */
    private Punto getPuntoCentro(int x, int y, int tamanio){
        int xCentro = x + (tamanio / 2);
        int yCentro = y + (tamanio / 2);
        return new Punto(xCentro, yCentro);
    }


    /**
     * Método para cargar el arbol de acuerdo al plano ingresado
     * @param cadenaPlano 
     */
    public void cargarArbol(String cadenaPlano){
        Arbol arbol = cargarNodosArbol(new StringBuffer(cadenaPlano));
        principal = arbol.getPrincipal();
    }


    /**
     * Método recursivo para cargar los nodos del arbol de acuerdo a la cadena
     * con los cortes del plano y sus valores
     * @param plan
     * @return Objeto arbol con los nodos cargados
     */
    public Arbol cargarNodosArbol(StringBuffer plan){
        //Pintamos el arblo si la cadena no es vacia
        if(plan.length() > 0){
            char caracter = plan.charAt(0);
            plan.deleteCharAt(0);
            //Si el caracter es - o |, llamamos recursivamente el metodo para crear los
            //arboles por derecha y por izquierda
            if(caracter == '-' || caracter == '|'){
                Arbol arbolIzquierda = cargarNodosArbol(plan);
                Arbol arbolDerecha = cargarNodosArbol(plan);
                return new Arbol(String.valueOf(caracter),arbolIzquierda,arbolDerecha);
            }
            return new Arbol(String.valueOf(caracter), null, null);
        }
        return null;
    }

    public void setPrincipal(Nodo nodo){
        principal = nodo;
    }

    public Nodo getPrincipal(){
        return principal;
    }
    
}
